package me.zhengjie.lift;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.agvSend.AgvSendService;
import me.zhengjie.nettydemo.lift.LiftStatusVo;
import me.zhengjie.nettydemo.mapper.LiftNettyMapper;
import me.zhengjie.nettydemo.nettty.LiftCenter;
import me.zhengjie.pojo.dto.LiftRequestDto;
import me.zhengjie.pojo.dto.NoticeAgvDto;
import me.zhengjie.pojo.entity.Lift;
import me.zhengjie.pojo.entity.LiftTask;
import me.zhengjie.pojo.entity.LiftTaskHistory;
import me.zhengjie.pojo.entity.LiftWaitPoint;
import me.zhengjie.service.impl.LiftTaskServiceImpl;
import me.zhengjie.sqlservilce.LiftSqlService;
import me.zhengjie.sqlservilce.LiftTaskHistoryService;
import me.zhengjie.sqlservilce.LiftTaskSqlService;
import me.zhengjie.sqlservilce.LiftWaitPointService;
import me.zhengjie.state.LiftTaskStatus;
import me.zhengjie.state.TaskType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;


@Slf4j
@Component
@RequiredArgsConstructor
public class LiftStatusMachine {
    private static final Logger liftLog = LoggerFactory.getLogger("liftTask");

    private final LiftTaskSqlService liftTaskService;

    private final AgvSendService agvSendService;

    private final LiftWaitPointService liftWaitPointService;

    private final LiftTaskHistoryService liftTaskHistoryService;

    private final LiftNettyMapper liftNettyMapper;

    private final LiftCenter liftCenter;
//    private final ThreadPoolTaskScheduler taskScheduler;

    private final LiftTaskServiceImpl liftTaskServiceImpls;

    private final LiftSqlService liftSqlService;

    // 电梯任务状态机
    //定时任务
    @Transactional
    @Scheduled(fixedDelay = 1000)
    public void startLiftTask() throws Exception {
        try {
            List<LiftTask> liftTaskList = liftTaskService.selectAll();
            for (LiftTask liftTask : liftTaskList) {
                Integer id = liftTask.getId();
                String taskCode = liftTask.getTaskCode();
                String liftCode = liftTask.getLiftCode();
                String status = liftTask.getStatus();
                String startFloor = liftTask.getStartFloor();
                Integer startFloorInt = Integer.valueOf(startFloor);
                String endFloor = liftTask.getEndFloor();
                Integer endFloorInt = Integer.valueOf(endFloor);
                String liftStorage = liftTask.getLiftStorage();
                LocalDateTime updateTime = liftTask.getUpdateTime();
                if (TaskType.NEW_FLOOR.getMessage().equals(liftCode)) {
                    try {
                        Lift lift = liftSqlService.selectLiftCode(liftCode);
                        Integer liftMode = lift.getLiftMode();
                        if (liftMode.equals(1)) {
                        //todo 电梯回复报文都为模拟，已电梯文档为准
                        switch (status) {
                            case "0":
                                LiftStatusVo liftInfo1 = liftNettyMapper.findLiftInfo(liftCode);
                                liftLog.info("查询出电梯状态：{}", liftInfo1);
                                int currentFloor = liftInfo1.getCurrentFloor();
                                //电梯在起始楼层，发送开门指令
                                Integer storageCallLiftFrame = liftNettyMapper.getStorageCallLiftFrame(liftCode);
                                if (storageCallLiftFrame != null && storageCallLiftFrame == 1) {
                                    boolean b = liftTaskService.updateStatus(id, LiftTaskStatus.RECEIVED_CALL.getCode());
                                    liftLog.info("呼叫电梯返回值：{}", b);
                                    if (!b) {
                                        liftLog.error("修改电梯任务状态为1，数据库异常，回滚");
                                        throw new RuntimeException("修改电梯任务状态为1，数据库异常，回滚");
                                    }
                                } else {
                                    boolean b = liftCenter.callLift(liftCode, startFloorInt);
                                    if (!b) {
                                        liftLog.error("状态1，发送开门指令失败");
                                        throw new RuntimeException("状态1，发送开门指令失败");
                                    }
                                }
//                                else {
//                                    //状态为0{任务已生成，未执行，下发呼叫电梯指令}
//                                    liftLog.info("目前任务状态：任务已生成，未执行，任务状态:{}", liftTask);
//                                    //呼叫电梯成功，修改状态
//                                    Integer storageCallLiftFrame = liftNettyMapper.getStorageCallLiftFrame(liftCode);
//                                    if (storageCallLiftFrame !=null && storageCallLiftFrame == 1) {
//                                        boolean b = liftTaskService.updateStatus(id, LiftTaskStatus.RECEIVED_CALL.getCode());
//                                        if (!b){
//                                            liftLog.error("修改电梯任务状态为1，数据库异常，回滚");
//                                            throw new RuntimeException("修改电梯任务状态为1，数据库异常，回滚");
//                                        }
//                                    }else {
//                                        //呼叫电梯
//                                        boolean b = liftCenter.callLift(liftCode, startFloorInt);
//                                        if (!b){
//                                            liftLog.error("发送呼叫电梯指令失败");
//                                            throw new RuntimeException("发送呼叫电梯指令失败");
//                                        }
//                                    }
//                                }
                                break;
                            case "1":
                                //状态为1{电梯已收到呼叫指令，判断电梯是否到达目标楼层，并开门到位}
                                LiftStatusVo liftInfo = liftNettyMapper.findLiftInfo(liftCode);
                                if (liftInfo != null) {
                                    if (liftInfo.getCurrentFloor() == startFloorInt && liftInfo.getLiftDoorStatus() == 2) {
                                        boolean b = liftTaskService.updateStatus(id, LiftTaskStatus.ARRIVE_START_FLOOR_AND_OPEN_DOOR.getCode());
                                        if (!b) {
                                            liftLog.error("修改电梯任务状态为2，数据库异常，回滚");
                                            throw new RuntimeException("修改电梯任务状态为2，数据库异常，回滚");
                                        }
                                    }
                                }
                                //判断1分钟未执行到位回滚
                                LocalDateTime now = LocalDateTime.now();
                                long seconds = Duration.between(updateTime, now).getSeconds();
                                if (seconds > 60) {
                                    liftLog.info("一分钟后电梯未到达其实楼层并开门到位，数据回滚到0");
                                    boolean b = liftTaskService.updateStatus(id, LiftTaskStatus.TASK_UNEXECUTED.getCode());
                                    if (!b) {
                                        liftLog.error("修改电梯任务状态为0，数据库异常，回滚");
                                        throw new RuntimeException("修改电梯任务状态为0，数据库异常，回滚");
                                    }
                                }
                                break;
                            case "2":
                                //状态为2{电梯到达起始楼层并开门到位，通知AGV进入电梯储位}
                                //下发AGV进入电梯储位，执行，进电梯储位
                                NoticeAgvDto noticeAgvDto = new NoticeAgvDto();
                                noticeAgvDto.setEqptCode(liftCode);
                                noticeAgvDto.setTaskCode(taskCode);
                                //todo 进入电梯，枚举不清楚
                                noticeAgvDto.setActionStatus("3");
//                                noticeAgvDto.setSiteCode(liftStorage);
                                boolean b3 = agvSendService.noticeAgv(noticeAgvDto);
                                if (b3) {
                                    liftLog.info("通知AGV进入电梯储位成功，修改状态");
                                    //通知AGV进入电梯储位，修改状态
                                    boolean b1 = liftTaskService.updateStatus(id, LiftTaskStatus.GET_ORDER_ENTER_LIFT.getCode());
                                    if (!b1) {
                                        liftLog.error("修改电梯任务状态为3，数据库异常，回滚");
                                        throw new RuntimeException("修改电梯任务状态为3，数据库异常，回滚");
                                    }
                                }
                                break;
                            case "3":
                                //agv进入电梯储位，上报给WCS
                                break;
                            case "4":
                                //agv是；上报到达电梯储位，下发关门至某层指令
                                Integer storageCloseDoorGoFloorFrame = liftNettyMapper.getStorageCallLiftFrame(liftCode);
                                if (storageCloseDoorGoFloorFrame != null && storageCloseDoorGoFloorFrame == 1) {
                                    //修改状态，关门至某层指令下发成功
                                    boolean b1 = liftTaskService.updateStatus(id, LiftTaskStatus.CLOSE_DOOR_TO_END_FLOOR.getCode());
                                    if (!b1) {
                                        liftLog.error("修改电梯任务状态为5，数据库异常，回滚");
                                        throw new RuntimeException("修改电梯任务状态为5，数据库异常，回滚");
                                    }
                                } else {
                                    //下发关门至某层指令
                                    boolean b = liftCenter.callLift(liftCode, endFloorInt);
                                    if (!b) {
                                        liftLog.error("发送关门至某层指令失败");
                                        throw new RuntimeException("发送关门至某层指令失败");
                                    }
                                }
                                break;
                            case "5":
                                //关门至某层指令，判断电梯是否到达终点楼层并开门到位
                                LiftStatusVo liftInfo2 = liftNettyMapper.findLiftInfo(liftCode);
                                if (liftInfo2 != null) {
                                    if (liftInfo2.getCurrentFloor() == endFloorInt && liftInfo2.getLiftDoorStatus() == 2) {
                                        //电梯到达终点楼层并开门到位
                                        boolean b1 = liftTaskService.updateStatus(id, LiftTaskStatus.ARRIVE_END_FLOOR_AND_OPEN_DOOR.getCode());
                                        if (!b1) {
                                            liftLog.error("修改电梯任务状态为6，数据库异常，回滚");
                                            throw new RuntimeException("修改电梯任务状态为5，数据库异常，回滚");
                                        }
                                    }
                                }
                                LocalDateTime now1 = LocalDateTime.now();
                                long seconds1 = Duration.between(updateTime, now1).getSeconds();
                                if (seconds1 > 60) {
                                    liftLog.info("一分钟后电梯未到达终点楼层并开门到位，数据回滚到0");
                                    boolean b = liftTaskService.updateStatus(id, LiftTaskStatus.ARRIVE_STORAGE_POINT.getCode());
                                    if (!b) {
                                        liftLog.error("修改电梯任务状态为0，数据库异常，回滚");
                                        throw new RuntimeException("修改电梯任务状态为0，数据库异常，回滚");
                                    }
                                }
                                break;
                            case "6":
                                //电梯到达终点楼层并开门到位，通知AGV离开电梯
                                //下发AGV离开电梯储位，执行，离开电梯储位
                                NoticeAgvDto noticeAgvDto1 = new NoticeAgvDto();
                                noticeAgvDto1.setEqptCode(liftCode);
                                noticeAgvDto1.setTaskCode(taskCode);
                                //todo 离开电梯，枚举不清楚
                                noticeAgvDto1.setActionStatus("4");
//                                noticeAgvDto1.setSiteCode(liftStorage);
                                boolean b2 = agvSendService.noticeAgv(noticeAgvDto1);
                                if (b2) {
                                    liftLog.info("通知AGV进入电梯储位成功，修改状态");
                                    //通知AGV进入电梯储位，修改状态
                                    boolean b1 = liftTaskService.updateStatus(id, LiftTaskStatus.GET_ORDER_LEAVE_LIFT.getCode());
                                    if (!b1) {
                                        liftLog.error("修改电梯任务状态为7，数据库异常，回滚");
                                        throw new RuntimeException("修改电梯任务状态为3，数据库异常，回滚");
                                    }
                                }
                                break;
                            case "7":
                                //通知AGV离开电梯指令，上报WCS离开电梯
                                break;
                            case "8":
                                //agv离开电梯储位，判断电梯是否要关门，同楼层有任务不用关门，删除任务，移到历史表
                                List<LiftWaitPoint> liftWaitPointList = liftWaitPointService.selectFloor(endFloor);
                                if (liftWaitPointList.isEmpty()) {
                                    Integer storageCloseDoorFrame = liftNettyMapper.getStorageCloseDoorFrame(liftCode);
                                    if (storageCloseDoorFrame != null && storageCloseDoorFrame == 1) {
                                        LiftTaskHistory liftTaskHistory = new LiftTaskHistory();
                                        BeanUtils.copyProperties(liftTask, liftTaskHistory);
                                        liftTaskHistory.setId(null);
                                        boolean b = liftTaskHistoryService.insertLiftTask(liftTaskHistory);
                                        if (!b) {
                                            liftLog.error("将任务添加到历史表失败，数据回滚");
                                            throw new RuntimeException("将任务添加到历史表失败，数据回滚");
                                        }
                                        //删除电梯任务
                                        boolean b1 = liftTaskService.deleteId(id);
                                        if (!b1) {
                                            liftLog.error("删除任务失败");
                                            throw new RuntimeException("删除任务失败");
                                        }
                                    } else {
                                        //关门
                                        boolean b = liftCenter.closeDoor(liftCode);
                                        if (!b) {
                                            liftLog.error("发送关门指令失败");
                                            throw new RuntimeException("发送关门指令失败");
                                        }
                                    }
                                } else {
                                    LiftTaskHistory liftTaskHistory = new LiftTaskHistory();
                                    BeanUtils.copyProperties(liftTask, liftTaskHistory);
                                    liftTaskHistory.setId(null);
                                    boolean b = liftTaskHistoryService.insertLiftTask(liftTaskHistory);
                                    if (!b) {
                                        liftLog.error("将任务添加到历史表失败，数据回滚");
                                        throw new RuntimeException("将任务添加到历史表失败，数据回滚");
                                    }
                                    //删除电梯任务
                                    boolean b1 = liftTaskService.deleteId(id);
                                    if (!b1) {
                                        liftLog.error("删除任务失败");
                                        throw new RuntimeException("删除任务失败");
                                    }
                                }
                                break;
                            //                    case "9":
                            //                        //通知电梯关门。当前楼层有任务不关门
                            //                        break;
                            default:
                                break;
                        }
                    }
                    } catch (Exception e) {
                        LiftStatusMachine.liftLog.error("catch报错日志", e);
                        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    }
                }else if (TaskType.PAST_FLOOR.getMessage().equals(liftCode)){
                    if (LiftTaskStatus.GET_ORDER_ENTER_LIFT.getCode().equals(status)){
                        LocalDateTime now = LocalDateTime.now();
                        long seconds = Duration.between(updateTime, now).getSeconds();
                        if (seconds >60){
                            //再次发送发送开门指令
                            LiftRequestDto liftRequestDto = new LiftRequestDto();
                            liftRequestDto.setRequestId(liftTask.getWangLongTaskIId());
                            liftRequestDto.setPosition("out");
                            liftRequestDto.setEffectiveTime("99");
                            liftLog.info("AGV收到关门指令一直未进电梯再次发送关门指令:{}", liftRequestDto);
                            String s = liftTaskServiceImpls.sendOpenDoor(liftRequestDto);
                            //更新时间
                            boolean b = liftTaskService.updateStatus(id, LiftTaskStatus.GET_ORDER_ENTER_LIFT.getCode());
                            if (!b){
                                log.error("AGV进电梯等待时间过长,再次发送关门指令,数据库异常");
                                throw new RuntimeException("AGV进电梯等待时间过长,再次发送关门指令,数据库异常");
                            }
                        }
                    }else if (LiftTaskStatus.GET_ORDER_LEAVE_LIFT.getCode().equals(status)){
                        LocalDateTime now = LocalDateTime.now();
                        long seconds = Duration.between(updateTime, now).getSeconds();
                        if (seconds >60){
                            //再次发送发送开门指令
                            LiftRequestDto liftRequestDto = new LiftRequestDto();
                            liftRequestDto.setRequestId(liftTask.getWangLongTaskIId());
                            liftRequestDto.setPosition("in");
                            liftRequestDto.setEffectiveTime("99");
                            liftLog.info("AGV收到离开电梯指令,一分钟后未离开再次下发:{}", liftRequestDto);
                            String s = liftTaskServiceImpls.sendOpenDoor(liftRequestDto);
                            //更新时间
                            boolean b = liftTaskService.updateStatus(id, LiftTaskStatus.GET_ORDER_LEAVE_LIFT.getCode());
                            if (!b){
                                log.error("AGV进电梯等待时间过长,再次发送关门指令,数据库异常");
                                throw new RuntimeException("AGV进电梯等待时间过长,再次发送关门指令,数据库异常");
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}